package com.hse.application;

import com.meshnetics.gb.stack.mac.type.PANDescriptor;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * <code>NodeDescriptor</code> that was received from MAC must have a <code>MACPanDescriptor</code>
 *
 * @author Liliya Butorina
 * @zigbee.document ZIGBEE SPECIFICATION
 * @zigbee.version 053474r17
 * @zigbee.section 3.6.7 (p. 442)
 */
public class NodeDescriptor {
    private final int stackProfile;
    private final int zigBeeVersion;
    private final boolean isRouterCapable;
    private final int depth;
    private final boolean isEndDeviceCapable;
    private final long longPanId;
    private final int nwkUpdateId;

    private final PANDescriptor panDescriptor;

    private static final int BYTE_MASK = 0xFF;

    private static final byte MASK_F0 = (byte) 0xf0;
    private static final byte MASK_0F = (byte) 0x0f;
    private static final int OFFSET_4 = 4;
    //    private static final int OFFSET_5 = 5;
    private static final int OFFSET_7 = 7;

    private static final int PAYLOAD_LENGTH = 15;

    public static final int NWK_MIN_BEACON_PAYLOAD_LENGTH = 3;

    public NodeDescriptor(int stackProfile, int zigBeeVersion, boolean routerCapable, int depth,
                          boolean endDeviceCapable, long longPanId, int nwkUpdateId,
                          PANDescriptor panDescriptor) {
        this.panDescriptor = panDescriptor;
        this.stackProfile = stackProfile;
        this.zigBeeVersion = zigBeeVersion;
        isRouterCapable = routerCapable;
        this.depth = depth;
        isEndDeviceCapable = endDeviceCapable;
        this.longPanId = longPanId;
        this.nwkUpdateId = nwkUpdateId;
    }

    public NodeDescriptor(int stackProfile, int zigBeeVersion, boolean routerCapable, int depth,
                          boolean endDeviceCapable, long longPanId, int nwkUpdateId) {
        this(stackProfile, zigBeeVersion, routerCapable, depth, endDeviceCapable, longPanId, nwkUpdateId, null);
    }

    public NodeDescriptor(byte[] nwkBeaconPayload, PANDescriptor panDescriptor) {
        if (panDescriptor == null) {
            throw new IllegalArgumentException("NodeDescriptor must have panDescriptor");
        }

        this.panDescriptor = panDescriptor;

        ByteBuffer bb = ByteBuffer.wrap(nwkBeaconPayload).order(ByteOrder.LITTLE_ENDIAN);
        bb.get();

        byte b = bb.get(); // Stack & ZigBee version
        stackProfile = b & MASK_0F;
        zigBeeVersion = (b >> OFFSET_4) & MASK_0F;

        b = bb.get(); // router & depth & endDevice
        isRouterCapable = (((b >> 2) & 0x01) == 1);
        depth = (b >> 3) & MASK_0F;
        isEndDeviceCapable = (((b >> OFFSET_7) & 0x01) == 1);

        longPanId = bb.getLong();

        bb.position(bb.position() + 3); //TxOptions

        nwkUpdateId = bb.get() & BYTE_MASK;
    }

    public byte[] toBytes() {
        ByteBuffer bbf = ByteBuffer.allocate(PAYLOAD_LENGTH).order(ByteOrder.LITTLE_ENDIAN);
        bbf.put((byte) 0); //protocolId

        bbf.put((byte) (stackProfile | ((zigBeeVersion << OFFSET_4) & MASK_F0)));

        byte ed = 0;
        if (isRouterCapable) {
            ed |= 1 << 2;
        }
        ed |= (depth << 3);
        if (isEndDeviceCapable) {
            ed |= 1 << OFFSET_7;
        }

        bbf.put(ed);
        bbf.putLong(longPanId);

        // TxOptions. Always 0xFFFFFF
        bbf.put((byte) BYTE_MASK);
        bbf.put((byte) BYTE_MASK);
        bbf.put((byte) BYTE_MASK);

        bbf.put((byte) nwkUpdateId);

        return bbf.array();
    }

    public int getStackProfile() {
        return stackProfile;
    }

    public int getZigBeeVersion() {
        return zigBeeVersion;
    }

    public boolean isRouterCapable() {
        return isRouterCapable;
    }

    public int getDepth() {
        return depth;
    }

    public boolean isEndDeviceCapable() {
        return isEndDeviceCapable;
    }

    public long getLongPanId() {
        return longPanId;
    }

    public int getNwkUpdateId() {
//        if (nwkUpdateId != null) {
        return nwkUpdateId;
//        } else {
//            return 0;
//        }
    }

    public PANDescriptor getPanDescriptor() {
        return panDescriptor;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        NodeDescriptor nodeDescriptor = (NodeDescriptor) o;
        if (depth != nodeDescriptor.depth) {
            return false;
        }
        if (equalsCapable(nodeDescriptor)) {
            return false;
        }
        if (longPanId != nodeDescriptor.longPanId) {
            return false;
        }
        if (nwkUpdateId != nodeDescriptor.nwkUpdateId) {
            return false;
        }
        if (stackProfile != nodeDescriptor.stackProfile) {
            return false;
        }
        if (zigBeeVersion != nodeDescriptor.zigBeeVersion) {
            return false;
        }
        return true;
    }

    private boolean equalsCapable(NodeDescriptor nodeDescriptor) {
        return isEndDeviceCapable != nodeDescriptor.isEndDeviceCapable ||
                isRouterCapable != nodeDescriptor.isRouterCapable;
    }

    public int hashCode() {
        int result;
        result = stackProfile;
        result = 29 * result + zigBeeVersion;
        result = 29 * result + (isRouterCapable ? 1 : 0);
        result = 29 * result + depth;
        result = 29 * result + (isEndDeviceCapable ? 1 : 0);
        result = 29 * result + (int) (longPanId ^ (longPanId >>> 32));
        result = 29 * result + nwkUpdateId;
        return result;
    }

    public String toString() {
        return "NodeDescriptor{" +
                "longPanId=0x" + Long.toHexString(longPanId) +
                ", panDescriptor=" + panDescriptor +
                ", nwkUpdateId=" + nwkUpdateId +
                ", stackProfile=" + stackProfile +
                ", zigBeeVersion=" + zigBeeVersion +
                ", isRouterCapable=" + isRouterCapable +
                ", isEndDeviceCapable=" + isEndDeviceCapable +
                ", depth=" + depth +
                '}';
    }
}
